import time
import os
from multiprocessing import Process
from typing import Optional
from clogger import logger
from conf.settings import YAML_CONFIG
from datetime import datetime
from sysom_utils import SysomFramework, GClient
from lib.metric_type.metric_type import Metric, DiagnoseInfo
from lib.metric_manager import MetricManager

SYSOM_POLL_TIMEOUT = 20
SYSOM_POLL_INTERVAL = 1
SLEEP_INTERVAL = 1


class DiagnoseWorker(Process):

    g_client: Optional[GClient] = None

    def __init__(self, metric_manager: MetricManager, queue, parent_pid):
        super().__init__(daemon=True)
        self.metric_manager = metric_manager
        self.queue = queue
        self.parent_pid = parent_pid;

    @classmethod
    def get_gclient(cls):
        if cls.g_client is None:
            SysomFramework.init(YAML_CONFIG)
            cls.g_client = SysomFramework.gclient("sysom_diagnosis")
        return cls.g_client
    
    def check_if_parent_is_alive(self):
        try:
            os.kill(self.parent_pid, 0)
        except OSError:
            logger.info(f"Diagnose worker's parent {self.parent_pid} is exit")
            exit(0)

    # use metric description to get metric instance
    def _search_metric(self, diagnose_info: DiagnoseInfo) -> Metric:
        try:
            level = diagnose_info.level
            type = diagnose_info.type
            metric_description = diagnose_info.metric_description

            metric_list = self.metric_manager.registed_metric[level][type]
            for metric in metric_list:
                if metric.settings.description == metric_description:
                    return metric
        except Exception as e:
            logger.exception(e)
            return None
        return None

    # todo: use async function
    def send_diagnosis(self, diagnose_info: DiagnoseInfo):
        metric = self._search_metric(diagnose_info)
        if metric is None:
            logger.warning(f"Diagnose:Can not find metric"
                           f"instance of {diagnose_info.metric_escription}")
            return

        try:
            diag_input = metric.construct_diagnose_req(diagnose_info)
            retdiag = DiagnoseWorker.get_gclient() \
                                    .post("api/v1/tasks/", json=diag_input)

            retdiag_dict = retdiag.json()
            if retdiag_dict["success"] is True:
                taskid = retdiag_dict["data"]["task_id"]
                logger.info(f"Send diagnosis success: {taskid}")

                start_time = datetime.now()
                end_time = datetime.now()

                while True:
                    if (end_time - start_time).seconds >= SYSOM_POLL_TIMEOUT:
                        break
                    retdict_t = get_diagnose_result(taskid)
                    state = retdict_t["data"]["status"]
                    if state == "Success":
                        logger.info("Get diagnosis result success!")
                        metric.process_diagnose_req(diagnose_info,
                                                    retdict_t["data"]["result"]
                                                    )
                        break
                    if state == "Fail":
                        logger.info("Get diagnosis result failed!")
                        break
                    time.sleep(SYSOM_POLL_INTERVAL)
                    end_time = datetime.now()
        except Exception as e:
            logger.error(f"Get diagnosis of alarm "
                         f"{diagnose_info.alarm_id} failed: {e}")

    def run(self):
        logger.info(f'告警诊断下发守护进程PID： {os.getpid()}')

        while True:
            try:
                self.check_if_parent_is_alive()
                
                if self.queue.empty():
                    time.sleep(SLEEP_INTERVAL)
                    continue

                diagnose_info = self.queue.get()
                self.send_diagnosis(diagnose_info)

            except Exception as e:
                logger.exception(e)


def get_diagnose_result(taskid):
    retdict = {"data": {"status": "Failed"}}
    try:
        retdiag = DiagnoseWorker.get_gclient().get("api/v1/tasks/%s/" % taskid)
        retdict = retdiag.json()
    except BaseException:
        logger.info("get_diagnose_result exception!")
        pass
    return retdict
